// 自动类型推导
// auto和decltype decltype是auto的高级形式 更侧重于编译期的类型计算 常用在泛型中获取各种类型配合typedef/using使用更加方便
// auto虽然方便但它隐藏了真正的类型 不可滥用 一般for循环i等临时变量可以使用auto
#include <map>
#include <string>
#include <vector>
#include <iostream>

using namespace std;
// auto自动推导能力只能在初始化场合
auto hello = "hello"s;                               // C++14 能够推断出std::string类型 否则推断出的是const char[6]
std::map<int, std::string> m = {{1, "a"}, {2, "b"}}; // 不能用auto推断出来

class Auto
{
    // auto a = 10; // 类里不能使用自动推导
    decltype(hello) str = "helloworld"; //但是可以使用decltype
    using str_type = decltype(hello);   //也可以用别名
    str_type str1 = "helloworld";
};

auto auto_func()
{
    vector<int> vv = {1, 2, 3, 4, 5};
    for (const auto &i : vv)
    {
        cout << i << endl;
    }

    // auto最佳实践之三 用来结构数据到变量
    tuple a{"1", 2, 3.0f}; // C++17 模板参数推导特性
    auto [x, y, z] = a;    // 使用auto的好处在于如果a的类型变了 auto处不需要跟着修改
    cout << x << y << z << endl;

    map<string, string> map = {{"1", "z"}, {"2", "a"}};
    for (auto &[k, v] : map) // auto最佳实践之一 用于容器类型的迭代
    {
        cout << "key=" << k << " value=" << v << endl;
    }

    // auto总是推导出值类型绝不会推导出引用类型 auto&& 是特殊用法总是推导出引用类型
    auto x1 = 10;
    auto *x2 = &x1;
    auto &&x5 = x1;

    decltype(x1) a1;       // auto要求必须从表达式推导没有表达式的时候可以使用decltype带上表达式，这次可以直接声明变量而不需要赋值了 decltype通过变量推导出类型
    decltype(x1) &a2 = x1; // int & 引用类型必须赋值
    decltype(&x1) a3;
    decltype(x1) *a4;
    decltype(&x1) *a5; // int **
    a3 = &x1;          // 类型int *
    a4 = &x1;          // 类型int *

    // 通过using定义类型别名
    using int_ptr = decltype(&x1);  // int *
    using int_ref = decltype(x1) &; // int &

    // 还可以使用auto
    decltype(auto) a6 = x1;  //int &
    decltype(auto) a7 = &x1; //int *

    return 0;
}

void call_back(const string &str)
{
    cout << "callback " << str << endl;
}

void handle(decltype(&call_back) func) // decltype能够轻松获取到函数指针
{
    func("!!!");
}

//auto最佳实践之二 C++14之后推导函数返回值
auto get_auto() {
    return 100;
}

int main()
{
    decltype(&call_back) func = call_back; //函数指针类型
    func("123");


}